// Copyright 2011 Google Inc. All Rights Reserved. package com.google.appengine.tools.mapreduce.inputs; import com.google.appengine.api.blobstore.BlobKey; import com.google.appengine.api.blobstore.BlobstoreInputStream; import com.google.appengine.tools.mapreduce.InputReader; import com.google.appengine.tools.mapreduce.inputs.InputStreamIterator.OffsetRecordPair; import com.google.common.io.CountingInputStream; import java.io.BufferedInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Iterator; /** */ class BlobstoreInputReader extends InputReader<BlobstoreRecordKey, byte[]> { // --------------------------- STATIC FIELDS --------------------------- private static final long serialVersionUID = -1869136825803030034L; private static final int DEFAULT_BUFFER_SIZE = 10000; // ------------------------------ FIELDS ------------------------------ long startOffset; long endOffset; private String blobKey; private byte terminator; private long offset = 0L; private transient CountingInputStream input; private transient Iterator<OffsetRecordPair> recordIterator; // --------------------------- CONSTRUCTORS --------------------------- BlobstoreInputReader(String blobKey, long startOffset, long endOffset, byte terminator) throws IOException { this.blobKey = blobKey; this.startOffset = startOffset; this.endOffset = endOffset; this.terminator = terminator; createStreams(); } // ------------------------ INTERFACE METHODS ------------------------ // --------------------- Interface Iterator --------------------- @Override public boolean hasNext() { return recordIterator.hasNext(); } @Override public KeyValue<BlobstoreRecordKey, byte[]> next() { OffsetRecordPair next = recordIterator.next(); BlobstoreRecordKey key = new BlobstoreRecordKey( new BlobKey(blobKey), startOffset + offset + next.getOffset()); byte[] value = next.getRecord(); return KeyValue.of(key, value); } // ------------------------ IMPLEMENTING METHODS ------------------------ @Override public double getProgress() { double currentOffset = (double) (offset + input.getCount()); return currentOffset / (double) (endOffset - startOffset); } // -------------------------- INSTANCE METHODS -------------------------- private void createStreams() throws IOException { input = new CountingInputStream( new BufferedInputStream( new BlobstoreInputStream(new BlobKey(blobKey), startOffset + offset), DEFAULT_BUFFER_SIZE)); recordIterator = new InputStreamIterator(input, endOffset - startOffset - offset, startOffset != 0L && offset == 0L, terminator); } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { blobKey = (String) stream.readObject(); startOffset = stream.readLong(); endOffset = stream.readLong(); terminator = stream.readByte(); offset = stream.readLong(); createStreams(); } private void writeObject(ObjectOutputStream stream) throws IOException { stream.writeObject(blobKey); stream.writeLong(startOffset); stream.writeLong(endOffset); stream.writeByte(terminator); long newOffset = offset + (input == null ? 0 : input.getCount()); stream.writeLong(newOffset); } }